#include "VertexDeclaration.h"

using namespace Nezha;

u8 VertexDeclaration::msElementTypeCount[VET_MAX_ELEMENT_TYPES] =
{
	0,								//VET_NONE,
	1,								//VET_FLOAT1,
	2,								//VET_FLOAT2,
	3,								//VET_FLOAT3,
	4,								//VET_FLOAT4,
	1,								//VET_SHORT1,
	2,								//VET_SHORT2,
	3,								//VET_SHORT3,
	4,								//VET_SHORT4,
	1,								//VET_COLOR,
	1,								//VET_HALF1,
	2,								//VET_HALF2,
	3,								//VET_HALF3,
	4,								//VET_HALF4,
};

u8 VertexDeclaration::msElementUnitSize[VET_MAX_ELEMENT_TYPES] =
{
	0,								//VET_NONE = 0,
	sizeof(float),					//VET_FLOAT1,
	sizeof(float),					//VET_FLOAT2,
	sizeof(float),					//VET_FLOAT3,
	sizeof(float),					//VET_FLOAT4,
	sizeof(short),					//VET_SHORT1,
	sizeof(short),					//VET_SHORT2,
	sizeof(short),					//VET_SHORT3,
	sizeof(short),					//VET_SHORT4,
	sizeof(u32),					//VET_COLOR,
	sizeof(short),					//VET_HALF1,
	sizeof(short),					//VET_HALF2,
	sizeof(short),					//VET_HALF3,
	sizeof(short),					//VET_HALF4,
};

u8 VertexDeclaration::msElementTypeSize[VET_MAX_ELEMENT_TYPES] =
{
	0,								//VET_NONE = 0,
	sizeof(float),					//VET_FLOAT1,
	sizeof(float)*2,				//VET_FLOAT2,
	sizeof(float)*3,				//VET_FLOAT3,
	sizeof(float)*4,				//VET_FLOAT4,
	sizeof(short),					//VET_SHORT1,
	sizeof(short)*2,				//VET_SHORT2,
	sizeof(short)*3,				//VET_SHORT3,
	sizeof(short)*4,				//VET_SHORT4,
	sizeof(u32),					//VET_COLOR,
	sizeof(short),					//VET_HALF1,
	sizeof(short)*2,				//VET_HALF2,
	sizeof(short)*3,				//VET_HALF3,
	sizeof(short)*4,				//VET_HALF4,
};

VertexDeclaration::VertexDeclaration()
	:mStride(0)
{
}

VertexDeclaration::~VertexDeclaration()
{
	// TODO unbind to device?
}

const VertexElement* VertexDeclaration::getElementByVS(VertexSementic vs, u32 index) const
{
	u32 numVE = numElements();

	if(vs == VS_TEXCOORD || vs == VS_COLOR)
	{
		int n = 0;
		for(u32 i = 0; i < numVE; i++)
		{
			if(mElements[i].Sementic == vs)
			{
				n = i;
				break;
			}
		}

		if(n + index < numVE)
		{
			return &mElements[n + index];
		}
	}
	else
	{
		for(u32 i = 0; i < numVE; i++)
		{
			if(mElements[i].Sementic == vs)
			{
				return &mElements[i];
			}
		}
	}

	return NULL;
}

const VertexElement* VertexDeclaration::addElement(u32 streamIndex, VertexSementic sementic,
	VertexElementType vt, u32 usageIndex, bool needUpdateStride)
{
	if(numElements() >= NZ_MAX_VERTEX_ATTRS)
		return NULL;

	VertexElement ve;
	ve.StreamIndex = streamIndex;
	ve.Sementic = sementic;
	ve.Type = vt;
	ve.UsageIndex = usageIndex;
	ve.Offset = computeOffset(numElements());
	
	mElements.push_back(ve);

	if(needUpdateStride)
	{
		this->updateStride();
	}

	return &mElements[numElements() - 1];
}

const VertexElement* VertexDeclaration::addElement(u32 streamIndex, u16 offset, VertexSementic sementic,
	VertexElementType vt, u32 usageIndex, bool needUpdateStride)
{
	if(numElements() >= NZ_MAX_VERTEX_ATTRS)
		return NULL;

	VertexElement ve;
	ve.StreamIndex = streamIndex;
	ve.Sementic = sementic;
	ve.Type = vt;
	ve.UsageIndex = usageIndex;
	ve.Offset = offset;

	mElements.push_back(ve);

	if(needUpdateStride)
	{
		this->updateStride();
	}

	return &mElements[numElements() - 1];
}

u16 VertexDeclaration::computeOffset(u32 index)
{
	u16 offset = 0;
	for(u32 i = 0; i < index; i++)
	{
		offset += msElementTypeSize[mElements[i].Type];
	}

	return offset;
}

u32 VertexDeclaration::updateStride()
{
	mStride = 0;
	u32 numVE = numElements();
	for(u32 i = 0; i < numVE; i++)
	{
		mStride += msElementTypeSize[mElements[i].Type];
	}

	return mStride;
}

void VertexDeclaration::clear()
{
	mElements.clear();
	mStride = 0;
}

VertexDeclaration& VertexDeclaration::operator = (const VertexDeclaration& vd)
{
	mElements = vd.mElements;
	mStride = vd.mStride;

	return *this;
}

u8 VertexDeclaration::ElementTypeCount(VertexElementType vt)
{
	return msElementTypeCount[vt];
}


u8 VertexDeclaration::ElementUnitSize(VertexElementType vt)
{
	return msElementUnitSize[vt];
}

u8 VertexDeclaration::ElementTypeSize(VertexElementType vt)
{
	return msElementTypeSize[vt];
}